package com.zzzzzz.app.account.service;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.zzzzzz.app.account.UserActiveException;
import com.zzzzzz.app.account.UserPasswordException;
import com.zzzzzz.app.account.dao.UserDao;
import com.zzzzzz.app.account.model.bo.UserBo;
import com.zzzzzz.app.account.model.bo.UserRoleBo;
import com.zzzzzz.app.account.model.po.User;
import com.zzzzzz.common.util.Constants;
import com.zzzzzz.common.util.StringPool;
import com.zzzzzz.common.util.Validator;
import com.zzzzzz.core.dao.search.SearchContainer;
import com.zzzzzz.core.exception.SystemException;
import com.zzzzzz.core.exception.ZzzzzzException;
import com.zzzzzz.core.model.bo.EmailSend;
import com.zzzzzz.core.model.po.Ticket;
import com.zzzzzz.core.service.MailService;
import com.zzzzzz.core.service.TicketManager;

@Service
public class UserManager {
	
	@Resource
	private UserDao userDao;
	@Resource
	private UserRoleManager userRoleManager;
	@Resource
	private TicketManager ticketManager;
	@Resource
	private MailService mailService;
	
	public void add(User user) throws SystemException {
		user.setCreateDate(new Date());
		user.setStatus(1);
		user.setPassword(new Sha256Hash(user.getPassword()).toHex());
		userDao.add(user);
	}

	@Transactional
	public void addAndSendAccountActivationEmail(User user, HttpServletRequest request) throws SystemException {
		add(user);
		sendAccountActivationEmail(user, request);
	}
	
	public void editStatus(Long id,Integer status) {
		userDao.editStatus(id, status);
	}

	public void editProfileBasic(User user) {
		userDao.editProfileBasic(user);
	}
	
	public void editProfileContact(User user) {
		userDao.editProfileContact(user);
	}
	
	public void editPortraitByUserId(Long id, String portrait) {
		userDao.editPortrait(id, portrait);
	}
	
	public void editPasswordByUserId(Long id, String currentPassword, String newPassword) {
		userDao.editPassword(id, currentPassword, newPassword);
	}

	public void resetPassword(Long userId, String newPassword, String reNewPassword) throws ZzzzzzException, SystemException {
		if (Validator.isNull(newPassword) || Validator.isNull(reNewPassword)) {
			throw new UserPasswordException(UserPasswordException.PASSWORD_INVALID);
		}
		if(!newPassword.equals(reNewPassword)) {
        	throw new UserPasswordException(UserPasswordException.PASSWORDS_DO_NOT_MATCH);
		}
		
		User user = userDao.findById(userId);
		String curEncPwd = user.getPassword();
		String newEncPwd = new Sha256Hash(newPassword).toHex();
		if(curEncPwd.equals(newEncPwd)) {
			throw new UserPasswordException(UserPasswordException.PASSWORD_SAME_AS_CURRENT);
		}
		userDao.resetPassword(userId, newEncPwd);
	}
	
	public void editEmailByUserId(Long id, String email, String password) {
		userDao.editEmail(id, email, password);
	}
	
	public User findById(Long id) throws ZzzzzzException, SystemException {
		return userDao.findById(id);
	}

	/**
	 * email已存在的返回true
	 * @param email
	 * @return
	 * @throws SystemException 
	 * @throws ZzzzzzException 
	 */
	public boolean isEmailExisting(String email) throws ZzzzzzException, SystemException {
		User user = this.findByEmail(email);
		if(user == null) return false;
		return true;
	}
	
	public User findByEmail(String email) throws ZzzzzzException, SystemException {
		return userDao.findByEmail(email);
	}
	
//	public Page<User> findAll(Page<User> page) {
//		return userDao.findAll(page);
//	}
	
	public User activate(long userId) throws SystemException, ZzzzzzException{
		User user = this.findById(userId);
		if(user.getStatus() == 1){
			this.editStatus(userId, 0);
		}
		return user;
	}
	
//	private String encodeActivate(Long userId,String email,Date createDate) throws Exception{
//		long timestamp = createDate.getTime();
//		StringBuilder activateSource = new StringBuilder(ACTIVATECODE);
//		activateSource.append(userId).append(email).append(timestamp);
//		return DigestUtils.md5Hex(activateSource.toString());
//	}
	
//	public User findCurrentUser() throws SystemException, ZzzzzzException {
//		Subject currentUser = SecurityUtils.getSubject();
//		Session session = currentUser.getSession();
//		User user = (User) session.getAttribute("currentUser");
//		if(user == null) {
//			Long currentUserId = (Long) currentUser.getPrincipal();
//			if( currentUserId != null ) {
//				user = findById(currentUserId);
//	    		session.setAttribute("currentUser", user);
//			}
//		}
//		return user;
//    }
	
	public User findCurrentUser() throws SystemException, ZzzzzzException {
		UserRoleBo userRoleBo = userRoleManager.findCurrentUser();
		if(userRoleBo != null) {
			return userRoleBo.getUser();
		}
		return null;
	}
	
	public Long findCurrentUserId(){
		return (Long) SecurityUtils.getSubject().getPrincipal();
	}
	
	public boolean isManager(){
		if(this.findCurrentUserId() == 1) return true;
		return false;
	}
	
	public boolean isCurrentUser(Long userId){
		Long currentUserId = findCurrentUserId();
		if(currentUserId != null && currentUserId == userId) return true;
		return false;
	}
	
	public boolean isSignedIn(){
		Subject currentUser = SecurityUtils.getSubject();
		return currentUser.isAuthenticated();
	}
	
	public void sendResetPasswordEmail(String email, HttpServletRequest request) throws ZzzzzzException, SystemException {
		User user = findByEmail(email);
		Date expirationDate = new Date(System.currentTimeMillis() + (24 * 60 * 60 * 1000));
		Ticket ticket = ticketManager.add(User.class.getName(), user.getUserId(), Constants.TICKET_CATEGORY.PASSWORD_RESET, expirationDate);
		String passwordResetURL = request.getServerName() + StringPool.COLON + request.getServerPort() + request.getContextPath() + "/user/reset_password/" + ticket.getKey();
		System.out.println("password reset url: " + passwordResetURL);
		
		// send reset password email
	}
	
	public void sendAccountActivationEmail(long userId, HttpServletRequest request) throws SystemException, ZzzzzzException {
		User user = userDao.findById(userId);
		sendAccountActivationEmail(user, request);
	}
	
	public void sendAccountActivationEmail(User user, HttpServletRequest request) throws SystemException {
		Date expirationDate = new Date(System.currentTimeMillis() + (7 * 24 * 60 * 60 * 1000));
		Ticket ticket = ticketManager.add(User.class.getName(), user.getUserId(), Constants.TICKET_CATEGORY.ACCOUNT_ACTIVATION, expirationDate);
		
		String activateURL = request.getServerName() + StringPool.COLON + request.getServerPort() + request.getContextPath() + "/user/activate/" + ticket.getKey();
		System.out.println("activate url: " + activateURL);
		EmailSend emailSend = new EmailSend();
		emailSend.setSendTo(user.getEmail());
		emailSend.setSendFrom(EmailSend.DEFAULT_SENDTO);
		emailSend.setSubject(EmailSend.REGISTER_ACTIVATE_SUBJECT);
		emailSend.setHtmlTemplate(EmailSend.REGISTER_ACTIVATE_TEMPLATE);
		Map<String,Object> htmlData = new HashMap<String,Object>();
		htmlData.put("username", user.getUsername());
		htmlData.put("activateURL", activateURL);
		emailSend.setHtmlData(htmlData);
		mailService.sendMail(emailSend);
	}
	
	public void login(String email, String password, boolean rememberMe) throws ZzzzzzException, SystemException {
		User user = userDao.findByEmail(email);
		if(user != null) {
	    	if(user.getStatus() == 1){
				throw new UserActiveException();
	    	}else{
	            Subject currentUser = SecurityUtils.getSubject();
	    		if (!currentUser.isAuthenticated()) {
		            UsernamePasswordToken token = new UsernamePasswordToken(email, password, rememberMe);
		        	currentUser.login(token);
	    		}
	    	}
		}
	}

	public List<User> findUserList(int start, int offset) throws SystemException {
		return userDao.findUserList(start, offset);
	}

	public int findALLCount() throws SystemException {
		return userDao.findALLCount();
	}
	
	public SearchContainer findUserList(int cur, String actionPrefixURL) throws SystemException {
		return findUserList(cur, actionPrefixURL, SearchContainer.DEFAULT_ITEMS_PER_PAGE, SearchContainer.DEFAULT_NUM_DISPLAY_PAGES, SearchContainer.DEFAULT_NUM_EDGE_PAGES);
	}
	
	public SearchContainer findUserList(int cur, String actionPrefixURL, int offset, int numDisplayPages, int numEdgePages) throws SystemException {
		int total = findALLCount();
		int start = calculateStart(cur, offset, total);
		List<User> users = findUserList(start, offset);
		List<UserBo> userBos = new ArrayList<UserBo>();
		for(User u : users) {
			UserBo userBo = new UserBo(u);
			userBos.add(userBo);
		}
		SearchContainer searchContainer = new SearchContainer(userBos, total, cur, actionPrefixURL, offset, numDisplayPages, numEdgePages);
		return searchContainer;
	}
	
	private int calculateStart(int cur, int offset, int total) {
		if(total == 0) {
			cur = 1;
		}else {
			cur = Math.min(cur, (int) Math.ceil((double) total/offset));
		}
		cur = Math.max(cur, 1);
		int start = (cur - 1) * offset;
		return start;
	}
	
}
